#include <Bitmap.hpp>
#include <Camera.hpp>
#include <Material.hpp>
#include <Ray.hpp>
#include <Sphere.hpp>
#include <glm/gtc/random.hpp>
#include <iostream>

static glm::vec3 ray_color(const Ray& ray, Hittable* world, int32_t depth) {
  HitRecord rec;

  if (depth <= 0) {
    return glm::vec3{};
  }

  if (world->Hit(ray, 0.001f, infinity, rec)) {
    if (!rec.material) {
      return glm::vec3{};
    }
    Ray scattered;
    glm::vec3 attenuation;
    if (rec.material->Scatter(ray, rec, attenuation, scattered)) {
      return attenuation * ray_color(scattered, world, depth - 1);
    }

    return glm::vec3{};
  }

  glm::vec3 unit_direction = glm::normalize(ray.Direction());
  float t = 0.5f * (unit_direction.y + 1.f);
  return (1.f - t) * glm::vec3(1.f, 1.f, 1.f) + t * glm::vec3(0.5f, 0.7f, 1.f);
}

int main(int argc, const char** argv) {
  // image
  auto bitmap = Bitmap::CreateBitmap(400, 16.f / 9.f);

  // world
  auto world = std::make_unique<HittableList>();

  auto material_ground =
      std::make_shared<Lambertian>(glm::vec3{0.8f, 0.8f, 0.0f});
  auto material_center =
      std::make_shared<Lambertian>(glm::vec3{0.7f, 0.3f, 0.3f});
  auto material_left = std::make_shared<Metal>(glm::vec3{0.8f, 0.8f, 0.8f});
  auto material_right = std::make_shared<Metal>(glm::vec3{0.8f, 0.6f, 0.2f});

  world->AddObject(std::make_shared<Sphere>(glm::vec3(0.f, -100.5f, -1.f),
                                            100.f, material_ground));
  world->AddObject(std::make_shared<Sphere>(glm::vec3(0.f, 0.f, -1.f), 0.5f,
                                            material_center));
  world->AddObject(std::make_shared<Sphere>(glm::vec3(-1.f, 0.f, -1.f), 0.5f,
                                            material_left));
  world->AddObject(std::make_shared<Sphere>(glm::vec3(1.f, 0.f, -1.f), 0.5f,
                                            material_right));

  Camera camera;
  int32_t samples_per_pixel = 100;
  int32_t max_depth = 50;

  for (uint32_t i = 0; i < bitmap->Width(); i++) {
    std::cout << "render line: " << i << std::endl;
    for (uint32_t j = 0; j < bitmap->Height(); j++) {
      glm::vec3 pixel_color{};
      for (int32_t s = 0; s < samples_per_pixel; s++) {
        auto u = float(i + glm::linearRand(0.f, 1.f)) / (bitmap->Width() - 1);
        auto v = float(j + glm::linearRand(0.f, 1.f)) / (bitmap->Height() - 1);
        Ray ray = camera.GetRay(u, v);

        pixel_color += ray_color(ray, world.get(), max_depth);
      }

      bitmap->setPixel(i, bitmap->Height() - 1 - j, pixel_color,
                       samples_per_pixel);
    }
  }

  bitmap->writeToFile("material_color.png");

  return 0;
}